﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web.Script.Serialization;
using System.Net;
using System.Net.Sockets;
using libaklys.Protocol;

namespace libaklys {
	public class Connection {
		private const string DEFAULT_HOST = "127.0.0.1";
		private const int DEFAULT_PORT = 57335;
		const int DEFAULT_BUFFER_SIZE = 2048;
		private JavaScriptSerializer JSON = new JavaScriptSerializer();
		private IPEndPoint point;
		private TcpClient conn;
		private NetworkStream stream;
		private UTF8Encoding encoder = new UTF8Encoding();
		public Connection(string hostname, int port) {
			Connect(hostname, port);
		}
		public Connection(IPAddress addr, int port) {
			Connect(addr, port);
		}
		public Connection(IPEndPoint point) {
			Connect(point);
		}
		private int BufferSize {
			get {
				return this.conn.ReceiveBufferSize;
			}
			set {
				this.conn.ReceiveBufferSize = value;
				this.conn.SendBufferSize = value;
			}
		}
		public void Connect(string hostname, int port) {
			Connect(Dns.GetHostEntry(hostname ?? DEFAULT_HOST), port);
		}
		public void Connect(IPHostEntry hostentry, int port) {
			if(hostentry.AddressList.Length<1) throw new ArgumentException("no addresses for host");
			Connect(hostentry.AddressList[0], port);
		}
		public void Connect(IPAddress addr, int port) {
			Connect(new IPEndPoint(addr, (port<1 ? DEFAULT_PORT : port)));
		}
		public void Connect(IPEndPoint point) {
			this.point = point;
			this.conn = new TcpClient();
			this.conn.Connect(point);
			this.stream = conn.GetStream();
			this.BufferSize = DEFAULT_BUFFER_SIZE;
		}
		public void Reconnect() {
			Close();
			Connect(this.point);
		}
		public void Close() {
			this.stream.Close();
			this.conn.Close();
		}

		private void TransmitRaw(string text) {
			byte[] buffer = encoder.GetBytes(text);
			stream.Write(buffer, 0, buffer.Length);
			stream.WriteByte(10);
			stream.Flush();
		}
		private string ReceiveRaw() {
			if( ! stream.DataAvailable) return null;
			byte[] buffer = new byte[this.BufferSize];
			StringBuilder text = new StringBuilder();
			int bytes_read = 0;
			do {
				bytes_read = stream.Read(buffer, 0, buffer.Length);
				text.AppendFormat("{0}", Encoding.UTF8.GetString(buffer, 0, bytes_read));
			}
			while(stream.DataAvailable);

			return text.ToString();
		}
		public void Transmit(Dictionary<string,object> data) {
			TransmitRaw(JSON.Serialize(data));
		}
		public Dictionary<string,object> Receive() {
			string text = ReceiveRaw();
			if(text==null) return null;

			return JSON.Deserialize<Dictionary<string,object>>(text);
		}
	}
}
